package com.company.finance.ddd.domain.core.voucher;

import com.company.finance.ddd.common.IRepository;
import com.company.finance.ddd.common.VoucherCreateException;
import com.company.finance.ddd.domain.core.bill.aggregate.*;
import com.company.finance.ddd.domain.core.voucher.aggregate.Voucher;
import com.company.finance.ddd.domain.core.voucher.event.*;
import com.company.finance.ddd.domain.core.voucher.event.publisher.EventPublisher;
import lombok.Builder;
import lombok.Data;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @author wanglei
 * @version $$Id: AccountingDocumentServiceImpl, v 0.1 2021/2/2 15:28 wanglei Exp $$
 */
@Getter
@Service
@Slf4j
public class VoucherService {

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private EventPublisher eventPublisher;

    @Autowired
    private IRepository<Voucher> voucherRepository;

    @Autowired
    private IRepository<BillAggregate> billRepository;

    @Autowired
    private IRepository<Voucher.VoucherItem> voucherItemRepository;

    @Transactional(rollbackFor = Exception.class)
    public void createVoucher(Long billId) {
        //TODO thread pool. 经jvvm分析99.9%的cpu时间被mysql-jdbc的一个fill()占用
        // ,so IO密集型就用线程池来处理下试试效果 经过修改mysql的tx_commit_flush参数后保存数据性能有了较大提升
        String msg = "Voucher create {}, billId -> [{}]";

        if (!query(VoucherQueryParam.builder().billId(billId).build()).isEmpty()) {
            log.info("The Voucher for bill[{}] is exists.", billId);
            return;
        }
        try{
            billRepository.get(billId).voucherService(this).createVoucherInstance()
                    .forEach(voucher -> {
                        voucherRepository.save(voucher);

                        log.info(msg, "succeeded", billId);

                        eventPublisher.publish(VoucherCreateSucceedEvent.builder()
                                .billId(billId).voucherId(voucher.getID()).build());
                    });
        } catch (VoucherCreateException e) {
            log.error(msg, "failed", billId, e);
            eventPublisher.publish(VoucherCreateFailedEvent.builder().billId(billId).build());
            throw new RuntimeException(e);
        }
    }

    public List<Voucher> query(VoucherQueryParam voucherQueryParam) {
        Map<String, Object> params = new HashMap<>(1);
        params.put("bill_id", voucherQueryParam.getBillId());
        return voucherRepository.query(params);
    }

    @Builder
    @Data
    public static class VoucherQueryParam {
        private final Long billId;
    }
}
